#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<assert.h>

typedef struct{
    int height;
    int width;
    double** data;}Matrix;

Matrix zeros_matrix(unsigned int h, unsigned int w);
void clear_matrix(Matrix mat);
void get_values_from_vector(Matrix *mat, double *vec);
void free_matrix(Matrix *mat);
void _free_matrix(Matrix mat);
// from local file
Matrix mat_add(Matrix *mat1, Matrix *mat2);
Matrix mat_subtruct(Matrix *mat1, Matrix *mat2);
Matrix mat_multiply(Matrix *mat1, Matrix *mat2);

int main(void){

    Matrix mat_a;
    unsigned int h = 5; 
    unsigned int w = 6;
    
    // Initilize a matrix
    mat_a = zeros_matrix(h, w);
    printf("Initialize a matrix with zeros. \n");
    
    // Print matrix info
    printf("Matrix height: %d. \n", mat_a.height);
    printf("Matrix width_: %d. \n", mat_a.width);
    
    // get an 1-d vector by random
    double vec[h*w];
    srand((unsigned int)time(NULL));
    for(int i=0; i<h*w; i++){
        vec[i] = rand()%10;}
    printf("Get 1-d vector: %f. \n", vec[h*w-1]);
    
    // set matrix values from vector
    get_values_from_vector(&mat_a, &vec[0]);  // input pointer: &vec[0]
    printf("Done! %f.\n", mat_a.data[h-1][w-1]);
    
    Matrix mat_b = mat_add(&mat_a, &mat_a);
    printf("Matrices addition done! %f. \n", mat_b.data[h-1][w-1]);
    Matrix mat_c = mat_subtruct(&mat_a, &mat_a);
    printf("Matrices subtruction done! %f. \n", mat_c.data[h-1][w-1]);
    
    Matrix mat_d = zeros_matrix(w, h);
    get_values_from_vector(&mat_d, &vec[0]);
    Matrix mat_e = mat_multiply(&mat_a, &mat_d);
    printf("Matrices multiplication done! %d-%d. \n", mat_e.height, mat_e.width);
    
    // Free memery
    free_matrix(&mat_a);
    free_matrix(&mat_b);
    free_matrix(&mat_c);
    free_matrix(&mat_d);
    free_matrix(&mat_e);
    // _free_matrix(mat_a);
    printf("Free all matrices. \n");
    
    return 0;}

Matrix zeros_matrix(unsigned int h, unsigned int w)
{
    Matrix mat; 
    int i;
    
    if(h<=0 || w<=0){
        printf("Error: height or width of matrix is less than 0!");}
    mat.height = h; 
    mat.width = w;
    
    mat.data = (double**)malloc(h*sizeof(double*));
    for(i=0; i<h; i++){
        *(mat.data + i) = (double*)malloc(w*sizeof(double));}
        
    clear_matrix(mat);
    return mat;
}

// 初始化矩阵为 0
void clear_matrix(Matrix mat){
    int h = mat.height;
    int w = mat.width;
    int i, j;
    for(i=0; i<h; i++){
        for(j=0; j<w; j++){
            mat.data[i][j] = 0;}}}

// 将一位数组中的数值输入到二维数组中
void get_values_from_vector(Matrix *mat, double *vec){
    int h = (*mat).height; int w = (*mat).width;
    int i, j;
    for(i=0; i<h; i++){
        for(j=0; j<w; j++){
            (*mat).data[i][j] = vec[i*w+j];}}}
            
// 后申请的内存先释放
void free_matrix(Matrix *mat){
    int i;
    for(i=0; i<mat->height; i++){
        free(mat->data[i]);}
    free(mat->data);}
void _free_matrix(Matrix mat){
    int i;
    for(i=0; i<mat.height; i++){
        free(*(mat.data + i));}
    free(mat.data);}

// matrix addition
Matrix mat_add(Matrix *mat1, Matrix *mat2){
    assert((mat1->height == mat2->height) && (mat1->width == mat2->width));
    Matrix mat;
    int h = mat1->height; int w = mat1->width;
    mat = zeros_matrix(h, w);
    int i, j;
    for(i=0; i<h; i++){
        for(j=0; j<w; j++){
            mat.data[i][j] = mat1->data[i][j] + mat2->data[i][j];}}
    return mat;}
// matrix suntruction
Matrix mat_subtruct(Matrix *mat1, Matrix *mat2){
    assert((mat1->height == mat2->height) && (mat1->width == mat2->width));
    Matrix mat;
    int h = mat1->height; int w = mat1->width;
    mat = zeros_matrix(h, w);
    int i, j;
    for(i=0; i<h; i++){
        for(j=0; j<w; j++){
            mat.data[i][j] = mat1->data[i][j] - mat2->data[i][j];}}
    return mat;}
// matrix multiplication
Matrix mat_multiply(Matrix *mat1, Matrix *mat2){
    assert(mat1->width == mat2->height);
    Matrix mat;
    int h1 = mat1->height; int w1 = mat1->width;
    int h2 = mat2->height; int w2 = mat2->width;
    mat = zeros_matrix(h1, w2);
    int i, j, k; double element;
    for(i=0; i<h1; i++){
        for(j=0; j<w2; j++){
            element = 0.0;
            for(k=0; k<w1; k++){
                element = element + mat1->data[i][k] * mat2->data[k][j];}
            mat.data[i][j] = element;}}
    return mat;}

    
